from cocos import scene, layer, sprite, batch, director, text
import config
from roles import Hero, Bullet, Enemy, ScoreLabel
from menus import GamePauseMenu, GamePauseStatusMenu
from events import fj_event_dispatcher
from cocos import collision_model
import pygame
from GameOverPage import GameOverScene

'''
程序运行页面
1. 定义游戏运行场景
   - 调用“背景层” - 背景滚动
   - 调用“游戏层” - logo、飞机、菜单等
   - 调用“暂停层” - 暂停当前界面
2. 背景层要求：
   - 背景滚动（事件调度）
3. 游戏层要求：
   - 飞机hero（能够鼠标控制 -- 处理事件调度）
   - 子弹（跟随飞机出现，并且自动前进）
   - 敌机（从上往下出现，并且自己前进）
'''


class BackgroundLayer(layer.Layer):
    def __init__(self):
        '''
        初始化背景层
        1. 添加背景图片
        2. 背景图片能够滚动播放（bg1/bg2向下调度）
        '''
        super(BackgroundLayer, self).__init__()

        # 图片1设置（定位）
        self.bg1 = sprite.Sprite('imgs/bg.png')
        self.bg1.image_anchor = (0, 0)
        self.bg1.position = (0, 0)
        self.add(self.bg1)

        # 图片2设置（定位）
        self.bg2 = sprite.Sprite('imgs/bg.png')
        self.bg2.image_anchor = (0, 0)
        self.bg2.position = (0, config.WIN_HEIGHT - 10)
        self.add(self.bg2)

        # ==== 添加定时任务：调度图片滚动
        self.schedule_interval(self.bg_scroll_schedule, interval=0.01)

    def on_game_pause(self):
        # print('Background receive on_game_pause')
        # self.pause(self)
        self.pause_scheduler()

    def bg_scroll_schedule(self, dt):
        if self.bg2.y > 0:
            self.bg1.y -= 1
            self.bg2.y -= 1
        else:
            self.bg1.position = (0, 0)
            self.bg2.position = (0, config.WIN_HEIGHT - 10)

    def on_enter(self):
        super(BackgroundLayer, self).on_enter()
        fj_event_dispatcher.push_handlers(self)

    def on_exit(self):
        super(BackgroundLayer, self).on_exit()
        fj_event_dispatcher.remove_handlers(self)


class GamePlayLayer(layer.Layer):
    # 用作鼠标事件用

    def __init__(self):
        '''
        初始化游戏层中元素，包括:
        1. 飞机hero（能够进行拖拽）
        2. 子弹（能够自己飞行）
        3. 敌机（能够自己飞行）
        4. 性能优化（子弹）：能够自己回收子弹，并将回收的子弹拿来使用
        '''
        super(GamePlayLayer, self).__init__()
        self.is_event_handler = True

        # ========飞机初始化【开始】========#
        self.hero = Hero()
        self.add(self.hero)
        # ========飞机初始化【结束】========#

        # =================子弹初始化【开始】=====================#
        # 初始化子弹(周期性产生子弹）
        self.schedule_interval(self.fire_bullet_schedule, interval=0.1)
        # 定义子弹回收区
        self.reusable_bullets = []
        # 子弹批处理
        self.bullets_batch = batch.BatchNode()
        self.add(self.bullets_batch)
        # ================子弹初始化【结束】===============#

        # =================敌机初始化【开始】===============#
        # 1.定义敌机回收区
        self.reusable_enemies = []
        # 2.定义敌机批处理
        self.enemies_batch = batch.BatchNode()
        self.add(self.enemies_batch)
        # 3.定义初始化敌机（周期性产生敌机）
        self.schedule_interval(self.create_enemy_schedule, interval=0.2)
        # =================敌机初始化【结束】===============#

        # ==========碰撞检测【开始】===========#
        # 1. 子弹与敌机的碰撞管理器
        self.enemy_bullet_collision = collision_model.CollisionManagerGrid(0, config.WIN_WIDTH,
                                                                           0, config.WIN_HEIGHT,
                                                                           110, 110)
        # 2. 飞机与敌机的碰撞管理器
        self.enemy_hero_collision = collision_model.CollisionManagerGrid(0, config.WIN_WIDTH,
                                                                         0, config.WIN_HEIGHT,
                                                                         self.hero.width * 1.25,
                                                                         self.hero.height * 1.25)
        # 3. 碰撞检测对象（活动飞机 + 活动子弹）
        self.active_bullets = []
        self.active_enemies = []

        # 4.周期性检测run_schedule
        self.schedule(self.collision_detect_schedule)
        # ==========碰撞检测【结束】===========#

        # 添加积分对象
        self.score_label = ScoreLabel()
        self.add(self.score_label)

    ################调度部分代码--开始#####################

    def fire_bullet_schedule(self, dt):
        # 从子弹池中获取子弹
        if len(self.reusable_bullets) > 0:
            bullet = self.reusable_bullets.pop()
        else:
            bullet = Bullet()
            self.bullets_batch.add(bullet)

        bullet.reset_status(self.hero.x, self.hero.y, self.hero.width, self.hero.height)

        # 将生成的子弹加入active_bullets中
        self.active_bullets.append(bullet)
        # print("active bullets are %d" % len(self.active_bullets))

    def create_enemy_schedule(self, dt):
        if len(self.reusable_enemies) > 0:
            enemy = self.reusable_enemies.pop()
        else:
            enemy = Enemy()
            self.enemies_batch.add(enemy)
        enemy.reset_status()
        self.active_enemies.append(enemy)
        # print("active enemies are %d" % len(self.active_enemies))

    def collision_detect_schedule(self, dt):
        # --------子弹与敌机碰撞检测（开始）-----------#
        # 1.清除当前碰撞
        self.enemy_bullet_collision.clear()
        # 2.将所有enemy加入碰撞
        for enemy in self.active_enemies:
            self.enemy_bullet_collision.add(enemy)
        # 3.将所有bullet加入碰撞
        for bullet in self.active_bullets:
            self.enemy_bullet_collision.add(bullet)
        # 4.得到碰撞结果
        enemy_bullet_result = self.enemy_bullet_collision.iter_all_collisions()
        # 子弹、敌机各自执行自己的碰撞结果
        for x, y in enemy_bullet_result:
            if type(x) == type(y):
                continue
            x.explode()
            y.explode()
            self.score_label.update_scores(100)
        # --------子弹与敌机碰撞检测（结束）-----------#

        # --------飞机与敌机碰撞检测（开始）-----------#
        # 1.清除飞机与敌机的碰撞
        self.enemy_hero_collision.clear()
        for enemy in self.active_enemies:
            self.enemy_hero_collision.add(enemy)
        enemy_hero_result = self.enemy_hero_collision.objs_colliding(self.hero)
        if len(enemy_hero_result) > 0:
            self.hero.explode()
        # ~~~~~~~~~飞机与敌机碰撞检测（结束）~~~~~~~~~~#

    ################调度部分代码--结束#####################

    # ================事件处理部分代码-开始====================#

    def on_mouse_drag(self, x, y, *args, **kwargs):
        '''
        :param x:鼠标传入x值
        :param y: 鼠标传入y值
        :param args: 占位
        :param kwargs: 占位
        :return: N/A

        其他：需要对鼠标范围进行限制，不能超过主屏幕
        '''
        if not self.hero.is_movable:
            return

        # =======鼠标范围处理逻辑=======#
        if x < self.hero.width // 2:
            x = self.hero.width // 2
        if x > config.WIN_WIDTH - self.hero.width // 2:
            x = config.WIN_WIDTH - self.hero.width // 2
        if y < self.hero.height // 2:
            y = self.hero.height // 2
        if y > config.WIN_HEIGHT - self.hero.height // 2:
            y = config.WIN_HEIGHT - self.hero.height // 2
        # ============================#

        self.hero.update_position(x, y)

    def on_mouse_press(self, x, y, *args, **kwargs):
        # =======解决按下鼠标时判断是否选中飞机的问题======#
        if self.hero.contains(x, y):
            self.hero.is_movable = True

    def on_mouse_release(self, *args, **kwargs):
        '''
        鼠标释放时，飞机的is_movable属性设为false
        :return: None
        '''
        self.hero.is_movable = False

    def on_enter(self):
        super(GamePlayLayer, self).on_enter()
        # 本层进入时，对监听事件进行管理
        fj_event_dispatcher.push_handlers(self)

    def on_exit(self):
        super(GamePlayLayer, self).on_exit()
        # 本层退出时，取消监听事件的管理
        fj_event_dispatcher.remove_handlers(self)
        pygame.mixer.music.stop()

    def on_bullet_reusable(self, bullet):
        '''
        接受到“on_bullet_reusable”事件后的处理
        1. 将子弹加入子弹池中
        2. 将子弹从active_bullets中取出
        :return: None
        '''
        self.reusable_bullets.append(bullet)
        # print("reusable bullets' number: %d" % len(self.reusable_bullets))
        try:
            self.active_bullets.remove(bullet)
        except:
            pass

    def on_enemy_reusable(self, enemy):
        '''
        收到消息后
        1. 将收到的敌机加入回收区中
        2. 将可重用的敌机从active_enemy中清除
        :param enemy:发送消息的敌机对象
        :return: None
        '''
        self.reusable_enemies.append(enemy)
        # print('reusable enemies are %d' % len(self.reusable_enemies))
        try:
            self.active_enemies.remove(enemy)
        except:
            pass

    def on_game_over(self):
        '''
        处理飞机发来的“on_game_over"消息
        1. 停止产生飞机
        2. 停止产生子弹
        3. 停止播放音乐
        4. 切换到场景GameOver中
        :return:
        '''
        self.unschedule(self.create_enemy_schedule)
        self.unschedule(self.fire_bullet_schedule)
        self.unschedule(self.collision_detect_schedule)

        pygame.mixer.music.stop()

        director.director.replace(GameOverScene(self.score_label.get_scores()))

    def on_game_pause(self):
        # 暂停层内所有调度
        # print("Gameplay Layer receiving on_game_pause message")
        self.pause_scheduler()

        # 活动的敌机暂停调度
        for enemy in self.active_enemies:
            enemy.pause_scheduler()

        # 活动的子弹暂停调度
        for bullet in self.active_bullets:
            bullet.pause_scheduler()

    def on_game_resume(self):
        self.resume_scheduler()

        # 活动的敌机恢复调度
        for enemy in self.active_enemies:
            enemy.resume_scheduler()

        # 活动的子弹恢复调度
        for bullet in self.active_bullets:
            bullet.resume_scheduler()

    # ================事件处理部分代码-结束====================#


class GamePauseStatusLayer(layer.ColorLayer):
    '''
    1. 设置颜色
    2. 添加菜单（恢复游戏，离开游戏）
    3. 本层进入是，屏蔽其他层的事件处理（on_mouse_press， on_mouse_drag等）
    '''

    def __init__(self):
        # 颜色设置
        super(GamePauseStatusLayer, self).__init__(0, 0, 0, 100)

        # 添加菜单
        game_pause_status_menu = GamePauseStatusMenu()
        self.add(game_pause_status_menu)

    def on_enter(self):
        '''
        暂停层进入的时候，其他层消息屏蔽
        :return:
        '''
        super(GamePauseStatusLayer, self).on_enter()
        game_play_scene = self.parent
        # print("game_play_scene children are: %d" % len(game_play_scene.get_children()))
        for node in game_play_scene.get_children():
            if node == self:
                continue
            director.director.window.remove_handlers(node)

    def on_exit(self):
        super(GamePauseStatusLayer, self).on_exit()

        ## 恢复其他层的消息处理
        game_play_scene = self.parent
        # print("game_play_scene children are: %d" % len(game_play_scene.get_children()))
        for node in game_play_scene.get_children():
            if node == self:
                continue
            if node.is_event_handler:
                director.director.window.push_handlers(node)


class GamePlayScene(scene.Scene):
    def __init__(self):
        super(GamePlayScene, self).__init__()

        # 添加背景层
        bg_layer = BackgroundLayer()
        self.add(bg_layer)

        # 添加游戏层
        game_play_layer = GamePlayLayer()
        self.add(game_play_layer)

        # 添加pause按钮
        game_pause_menu = GamePauseMenu()
        self.add(game_pause_menu)

    def on_enter(self):
        '''
        播放背景音乐
        :return:
        '''
        super(GamePlayScene, self).on_enter()

        pygame.mixer.init()
        pygame.mixer.music.load('sounds/bg_music.mp3')
        pygame.mixer.music.play(loops=-1)

        fj_event_dispatcher.push_handlers(self)

    def on_exit(self):
        super(GamePlayScene, self).on_exit()
        pygame.mixer.music.stop()
        fj_event_dispatcher.remove_handlers(self)

    def on_game_pause(self):
        # print('Gameplay scene receive on_game_pause message')
        self.game_pause_status_layer = GamePauseStatusLayer()
        length = len(self.get_children())
        self.add(self.game_pause_status_layer, z=length)

    def on_game_resume(self):
        self.remove(self.game_pause_status_layer)
